package com.gallsoft.games.bennyadv;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Random;
import java.util.StringTokenizer;

import com.gallsoft.games.bennyadv.Exit.ExitType;
import com.gallsoft.games.bennyadv.Platform;
import com.gallsoft.games.bennyadv.Enemy.EnemyType;
import com.gallsoft.games.bennyadv.Scenery.SceneryType;
import com.gallsoft.games.framework.FileIO;
import com.gallsoft.games.framework.Input;
import com.gallsoft.games.framework.gl.TextureRegion;
import com.gallsoft.games.framework.math.OverlapTester;
import com.gallsoft.games.framework.math.Vector2;

public class World {

    public interface WorldListener {

        public void jump();

        public void highJump();

        public void hit();

        public void coin();
    }

    public enum BobNextState {
        STOP,
        LEFT,
        RIGHT,
        WALK,
        CLIMB,
        JUMP,
        NOCHANGE
    }
    
    public static final float WORLD_WIDTH = 30;
    public static final float WORLD_HEIGHT = 25;
    public static final int WORLD_STATE_RUNNING = 0;
    public static final int WORLD_STATE_NEXT_LEVEL = 1;
    public static final int WORLD_STATE_GAME_OVER = 2;
    public static final int WORLD_STATE_DIED = 3;
    public static final Vector2 gravity = new Vector2(0, -12);

    public final Bob bob;
    public final List<Platform> platforms;
    public List<Platform> movingPlatforms;
    //public final List<Platform> movingPlatformsCurrent;
    public final List<SolidBlock> solidBlocks;
    public final List<LadderBlock> ladderBlocks;
    public List<Item> items;
    //public final List<Item> itemsCurrent;
    public final List<Enemy> enemies;
    public final List<Scenery> scenery;
    public List<Scenery> switches;
    //public final List<Scenery> switchesCurrent;
    public List<Scenery> doors;
    //public List<Scenery> doorsCurrent;
    public List<Scenery> keys;
    //public List<Scenery> keysCurrent;
    public final List<Scenery> spikes;
    public final List<DisplayMessage> messages;
    public final WorldListener listener;
    //public final Random rand;
    
    public TextureRegion background;

    public float heightSoFar;
    public long score;
    public int lives;
    public int itemsCollected;
    public int numItems;
    public int state;
    public float secTimer;
    public int timer;
    
    private Hashtable<String, TextureRegion> platformStyles = new Hashtable<String, TextureRegion>();
    private Hashtable<String, EnemyType> enemyStyles = new Hashtable<String, EnemyType>();
    private Hashtable<String, TextureRegion> sceneryStyles = new Hashtable<String, TextureRegion>();
    private Hashtable<String, ExitType> exitStyles = new Hashtable<String, ExitType>();
    private Hashtable<String, TextureRegion> backgroundStyles = new Hashtable<String, TextureRegion>();

    public Exit exit;
    private boolean onLadder = false;
    private boolean onMovingPlatform = false;
    private boolean itemMessage = false;
    private boolean switched = false;
    public int level;

    public World(WorldListener listener, FileIO assetDir, int nextLevel) {
        //int bobStartY = 5;
        //this.bob = new Bob(2, bobStartY + (Bob.BOB_HEIGHT / 2));
        this.bob = new Bob(0, 0);
        this.platforms = new ArrayList<Platform>();
        this.movingPlatforms = new ArrayList<Platform>();
        //this.movingPlatformsCurrent = new ArrayList<Platform>();
        this.items = new ArrayList<Item>();
        //this.itemsCurrent = new ArrayList<Item>();
        this.scenery = new ArrayList<Scenery>();
        this.solidBlocks = new ArrayList<SolidBlock>();
        this.ladderBlocks = new ArrayList<LadderBlock>();
        this.listener = listener;
        this.enemies = new ArrayList<Enemy>();
        this.switches = new ArrayList<Scenery>();
        //this.switchesCurrent = new ArrayList<Scenery>();
        this.doors = new ArrayList<Scenery>();
        this.keys = new ArrayList<Scenery>();
        this.spikes = new ArrayList<Scenery>();
        this.messages = new ArrayList<DisplayMessage>();
        
        backgroundStyles.put("SKY", Assets.backgroundRegion);
        backgroundStyles.put("NEON", Assets.backgroundRegion2);
        backgroundStyles.put("BLUE", Assets.backgroundRegion3);
        backgroundStyles.put("CASTLE", Assets.backgroundRegion4);
        
        platformStyles.put("platformBlock1", Assets.platformBlock1);
        platformStyles.put("platformBlock2", Assets.platformBlock2);
        platformStyles.put("platformBlock3", Assets.platformBlock3);
        platformStyles.put("platformBlock4", Assets.platformBlock4);
        platformStyles.put("platformBlock7", Assets.platformBlock7);
        platformStyles.put("platformBlock9", Assets.platformBlock9);
        platformStyles.put("platformBlock10", Assets.platformBlock10);
        platformStyles.put("platformBlock11", Assets.platformBlock11);
        platformStyles.put("platformBlock12", Assets.platformBlock12);
        platformStyles.put("platformBlock13", Assets.platformBlock13);
        platformStyles.put("platformBlock14", Assets.platformBlock14);
        platformStyles.put("platformBlock15", Assets.platformBlock15);
        platformStyles.put("platformBlock16", Assets.platformBlock16);
        
        platformStyles.put("movingPlatform1", Assets.movingPlatform1);
        platformStyles.put("movingPlatform2", Assets.movingPlatform2);
        platformStyles.put("movingPlatform3", Assets.movingPlatform3);
        platformStyles.put("movingPlatform4", Assets.movingPlatform4);
        
        platformStyles.put("solidBlock1", Assets.solidBlock1);                
        platformStyles.put("solidBlock2", Assets.solidBlock2);                
        platformStyles.put("solidBlock3", Assets.solidBlock3);                
        platformStyles.put("solidBlock4", Assets.solidBlock4);                
        platformStyles.put("ladderBlock1", Assets.ladderBlock1);        
        
        platformStyles.put("strawberry", Assets.strawberry);
        platformStyles.put("burger", Assets.burger);
        platformStyles.put("jellybean", Assets.jellybean);
        
        // Enemies
        enemyStyles.put("FLY", EnemyType.FLY);
        enemyStyles.put("SNAIL", EnemyType.SNAIL);
        enemyStyles.put("CRAB", EnemyType.CRAB);
        enemyStyles.put("EGG", EnemyType.EGG);
        enemyStyles.put("PINKSWEET", EnemyType.PINKSWEET);
        enemyStyles.put("BLUESWEET", EnemyType.BLUESWEET);
        enemyStyles.put("TOMATO", EnemyType.TOMATO);
        
        // Passive scenery
        sceneryStyles.put("GRASS", Assets.grass);
        sceneryStyles.put("STEM1", Assets.stem1);
        sceneryStyles.put("WHITE_FLOWER", Assets.whiteFlower);
        sceneryStyles.put("KETCHUP", Assets.ketchup);
        
        // Interactive scenery
        sceneryStyles.put("YELLOWSWITCH", Assets.yellowSwitch);
        sceneryStyles.put("REDSWITCH", Assets.redSwitch);
        sceneryStyles.put("GREENSWITCH", Assets.greenSwitch);

        sceneryStyles.put("REDDOOR", Assets.redDoor);
        sceneryStyles.put("REDKEY", Assets.redKey);
        sceneryStyles.put("ORANGEDOOR", Assets.orangeDoor);
        sceneryStyles.put("ORANGEKEY", Assets.orangeKey);
        sceneryStyles.put("GREENDOOR", Assets.greenDoor);
        sceneryStyles.put("GREENKEY", Assets.greenKey);
        sceneryStyles.put("YELLOWDOOR", Assets.yellowDoor);
        sceneryStyles.put("YELLOWKEY", Assets.yellowKey);
        sceneryStyles.put("PINKDOOR", Assets.pinkDoor);
        sceneryStyles.put("PINKKEY", Assets.pinkKey);
        
        sceneryStyles.put("SPIKE1", Assets.spike1);

        // Exits
        exitStyles.put("WOODEN_EXIT", ExitType.WOODEN_EXIT);
        exitStyles.put("KENNEL", ExitType.KENNEL);

        //this.itemsCollected = 0;
        //this.score = currentScore;
        //this.lives = lives;
        this.level = nextLevel;
        generateLevel(assetDir);

        numItems = items.size();
        this.heightSoFar = 0;
        this.state = WORLD_STATE_RUNNING;
        //this.timer = 120;
        this.secTimer = 0;
    }

    public void generateLevel(FileIO assetDir) {
        //float maxJumpHeight = Bob.BOB_JUMP_VELOCITY * Bob.BOB_JUMP_VELOCITY / (2 * -gravity.y);
        
        // Load level data
        BufferedReader in = null;
        String lineType;
        float x, y, velX, velY, min, max, width, angle;
        int len, type, facing;
        String style, style2, style3;
        Platform platform;
        SolidBlock solidBlock;
        LadderBlock ladderBlock;
        Item item;
        TextureRegion blockTexture, blockTexture2, blockTexture3;
        Vector2 enemyVelocity;
        EnemyType enemyType;
        //SceneryType sceneryType;
        ExitType exitType;
        boolean flip = false;
        boolean isMoving = false;
        ArrayList<String> styleDefinitions;
        
        try {
            
            in = new BufferedReader(new InputStreamReader(assetDir.readAsset("level.dat")));
            String line;
            
            // Find current level data
            while ((line = in.readLine()) != null) {
                if (!line.equals("")) {
                    StringTokenizer st = new StringTokenizer(line);
                    
                    lineType = st.nextToken();
                    
                    if (lineType.equals("LEVEL") && Integer.valueOf(st.nextToken()) == level) {
                        break;
                    }
                }
            }
            
            // Read current level data
            while ((line = in.readLine()) != null) {
                if (!line.equals("")) {
                    StringTokenizer st = new StringTokenizer(line);
                    
                    lineType = st.nextToken();

                    if (lineType.substring(0, 1).equals("#")) {
                        continue;
                    }
                    
                    if (lineType.equals("LEVEL")) {
                        break;
                    }
                    else if (lineType.equals("BACKGROUND")) {
                        style = st.nextToken();
                        background = backgroundStyles.get(style);
                    }
                    else if (lineType.equals("START")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        facing = Integer.valueOf(st.nextToken()).intValue();
                        
                        bob.setStartPosition(x, y, facing);
                    }
                    else if (lineType.equals("TIMER")) {
                        timer = Integer.valueOf(st.nextToken()).intValue();;
                    }
                    else if (lineType.equals("GROUND")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        len = Integer.valueOf(st.nextToken()).intValue();                        
                        style = st.nextToken();
                        type = Integer.valueOf(st.nextToken()).intValue();
                        
                        blockTexture = platformStyles.get(style);
                        
                        for (int i = 0; i < len; i++) {
                            platform = new Platform(type, x++, y, blockTexture, false, 0);
                            platforms.add(platform);
                        }                        
                    }
                    else if (lineType.equals("SOLID")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        len = Integer.valueOf(st.nextToken()).intValue();                        
                        style = st.nextToken();
                        
                        blockTexture = platformStyles.get(style);
                        
                        for (int i = 0; i < len; i++) {
                            solidBlock = new SolidBlock(x, y++, 1.0f, 1.0f, blockTexture);
                            solidBlocks.add(solidBlock);
                        }
                    }
                    else if (lineType.equals("LADDER")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        len = Integer.valueOf(st.nextToken()).intValue();                        
                        style = st.nextToken();
                        
                        blockTexture = platformStyles.get(style);
                        
                        for (int i = 0; i < len; i++) {
                            ladderBlock = new LadderBlock(x, y++, 1.0f, 1.0f, blockTexture);
                            ladderBlocks.add(ladderBlock);
                        }
                    }
                    else if (lineType.equals("PLATFORM")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        len = Integer.valueOf(st.nextToken()).intValue();
                        style = st.nextToken();
                        style2 = st.nextToken();
                        style3 = st.nextToken();                        
                        type = Integer.valueOf(st.nextToken()).intValue();
                        flip = Boolean.valueOf(st.nextToken()).booleanValue();
                        
                        blockTexture = platformStyles.get(style);
                        blockTexture2 = platformStyles.get(style2);
                        blockTexture3 = platformStyles.get(style3);
                        
                        createPlatform(x++, y, len, blockTexture, blockTexture2, blockTexture3, type, flip);
                    }
                    else if (lineType.equals("PLATFORMSINGLE")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        len = Integer.valueOf(st.nextToken()).intValue();
                        style = st.nextToken();
                        type = Integer.valueOf(st.nextToken()).intValue();
                        flip = Boolean.valueOf(st.nextToken()).booleanValue();
                        angle = Float.valueOf(st.nextToken()).floatValue();
                        
                        blockTexture = platformStyles.get(style);
                        
                        createPlatform(x++, y, len, blockTexture, type, flip, angle);
                    }
                    else if (lineType.equals("PLATFORMDEFINED")) {
                        boolean myFlip = false;
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        len = Integer.valueOf(st.nextToken()).intValue();
                        
                        styleDefinitions = new ArrayList<String>();
                        
                        for (int i = 0; i < len; i++)
                            styleDefinitions.add(st.nextToken());
                        
                        type = Integer.valueOf(st.nextToken()).intValue();
                        flip = Boolean.valueOf(st.nextToken()).booleanValue();
                        
                        for (int i = 0; i < len; i++) {
                            myFlip = (i == len - 1) ? flip : false;
                            blockTexture = platformStyles.get(styleDefinitions.get(i));
                            createPlatform(x++, y, blockTexture, type, myFlip);
                        }
                        
                    }
                    else if (lineType.equals("MOVINGPLATFORM")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        width = Integer.valueOf(st.nextToken()).intValue();
                        min = Float.valueOf(st.nextToken()).floatValue();
                        max = Float.valueOf(st.nextToken()).floatValue();
                        velX = Float.valueOf(st.nextToken()).floatValue();
                        style = st.nextToken();
                        isMoving = Boolean.valueOf(st.nextToken()).booleanValue();
                        
                        blockTexture = platformStyles.get(style);
                        MovingPlatform mv = new MovingPlatform(x, y, width, 1.0f, min, max, velX, blockTexture, isMoving);
                        movingPlatforms.add(mv);
                    }
                    else if (lineType.equals("ITEM")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        style = st.nextToken();
                        
                        blockTexture = platformStyles.get(style);
                        
                        item = new Item(x, y, blockTexture);
                        items.add(item);
                    }
                    else if (lineType.equals("SCENERY")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        style = st.nextToken();
                        
                        blockTexture = sceneryStyles.get(style);
                        
                        Scenery newScenery = new Scenery(x, y, 1.0f, 1.0f, blockTexture);
                        scenery.add(newScenery);
                    }
                    else if (lineType.equals("SWITCH")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        angle = Float.valueOf(st.nextToken()).floatValue();
                        style = st.nextToken();
                        
                        blockTexture = sceneryStyles.get(style);
                        
                        Scenery newScenery = new Scenery(x, y, 1.0f, 1.0f, angle, blockTexture);
                        switches.add(newScenery);
                    }
                    else if (lineType.equals("DOOR")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        style = st.nextToken();
                        
                        blockTexture = sceneryStyles.get(style);
                        
                        Scenery newScenery = new Scenery(x, y, 1.0f, 1.0f, blockTexture);
                        doors.add(newScenery);
                    }
                    else if (lineType.equals("KEY")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        style = st.nextToken();
                        
                        blockTexture = sceneryStyles.get(style);
                        
                        Scenery newScenery = new Scenery(x, y, 1.0f, 1.0f, blockTexture);
                        keys.add(newScenery);
                    }
                    else if (lineType.equals("SPIKE")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        style = st.nextToken();
                        
                        blockTexture = sceneryStyles.get(style);
                        
                        Scenery newScenery = new Scenery(x, y, 1.0f, 1.0f, blockTexture);
                        spikes.add(newScenery);
                    }
                    else if (lineType.equals("EXIT")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        style = st.nextToken();
                        
                        exitType = exitStyles.get(style);
                        
                        exit = new Exit(x, y, 2.0f, 2.0f, exitType);
                    }
                    else if (lineType.equals("ENEMY")) {
                        x = Float.valueOf(st.nextToken()).floatValue();
                        y = Float.valueOf(st.nextToken()).floatValue();
                        velX = Float.valueOf(st.nextToken()).floatValue();
                        velY = Float.valueOf(st.nextToken()).floatValue();
                        min = Float.valueOf(st.nextToken()).floatValue();
                        max = Float.valueOf(st.nextToken()).floatValue();
                        
                        style = st.nextToken();                        
                        enemyType = enemyStyles.get(style);

                        enemyVelocity = new Vector2(velX, velY);                       
                        Enemy enemy = new Enemy(x, y, 1.0f, 1.0f, enemyVelocity, enemyType);
                        
                        if (velX == 0)
                            enemy.setVerticalRange(min, max);
                        else
                            enemy.setHorizontalRange(min, max);
                            
                        enemies.add(enemy);

                    }
                }
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                if (in != null)
                    in.close();
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        //Scenery exit = new Scenery(1.0f, 18.0f, 2.0f, 2.0f, Scenery.SceneryType.EXIT);
        //scenery.add(exit);
    }

    public void update(float deltaTime, Input input, boolean[] touchControls) {
        secTimer += deltaTime;
        
        updateMessages(deltaTime);
        updateBob(deltaTime, input, touchControls);
        updatePlatforms(deltaTime);
        updateEnemies(deltaTime);
        updateCoins(deltaTime);
        
        if (secTimer > 1.0f) {
            if (bob.state != Bob.BOB_STATE_HIT) {
                timer -= 1;
                
                if (timer == 0) {
                    bob.hitEnemy();
                    listener.hit();
                
                    if (!itemMessage) {
                        messages.add(new DisplayMessage("Time up!", deltaTime, 4.0f, 190, 160));
                        itemMessage = true;
                    }
                }
            }
            secTimer = 0;
        }

        if (bob.state != Bob.BOB_STATE_HIT) {
            checkCollisions(deltaTime);
        }
        else
        	checkGameOver();
    }

    private void updateMessages(float deltaTime) {
        int len = messages.size();
        for (int i = 0; i < len; i++) {
            DisplayMessage message = messages.get(i);
            if (!message.update(deltaTime)) {
                messages.remove(i);
            }
        }        
    }
    
    private void updateBob(float deltaTime, Input input, boolean[] touchControls) {
        BobNextState newState = BobNextState.NOCHANGE;
        
        if (bob.state != Bob.BOB_STATE_FALL && bob.state != Bob.BOB_STATE_HIT) {
            if (!onLadder) {
                if (touchControls[2] || input.isKeyPressed(android.view.KeyEvent.KEYCODE_DPAD_RIGHT)) {
                    bob.velocity.x = Bob.BOB_MOVE_VELOCITY;
                    if (bob.state != Bob.BOB_STATE_JUMP)
                        newState = BobNextState.WALK;
                }
                else if (touchControls[1] || input.isKeyPressed(android.view.KeyEvent.KEYCODE_DPAD_LEFT)) {
                    bob.velocity.x = -Bob.BOB_MOVE_VELOCITY;
                    if (bob.state != Bob.BOB_STATE_JUMP)
                        newState = BobNextState.WALK;
                }
                else {
                    bob.velocity.x = 0;
                    if (bob.state != Bob.BOB_STATE_JUMP)
                        newState = BobNextState.STOP;
                }
                //checkMovingPlatformCollisions(deltaTime);
            }
            
            if (bob.state != Bob.BOB_STATE_JUMP) {
                if (touchControls[3] || input.isKeyPressed(android.view.KeyEvent.KEYCODE_DPAD_UP)) {
                    if (checkLadderCollsions(deltaTime, false)) {
                        bob.velocity.y = Bob.BOB_MOVE_VELOCITY;
                        newState = BobNextState.CLIMB;
                        onLadder = true;
                    }
                    else {
                        newState = BobNextState.STOP;
                        onLadder = false;
                    }
                }
                else if (touchControls[4] || input.isKeyPressed(android.view.KeyEvent.KEYCODE_DPAD_DOWN)) {
                    if (checkLadderCollsions(deltaTime, true)) {
                        bob.velocity.y = -Bob.BOB_MOVE_VELOCITY;
                        newState = BobNextState.CLIMB;
                        onLadder = true;
                    }
                    else {
                        newState = BobNextState.STOP;
                        onLadder = false;
                    }
                }
                else if (onLadder)
                    bob.velocity.y = 0;
            }

            checkMovingPlatformCollisions(deltaTime);

            if (!onLadder && bob.state != Bob.BOB_STATE_JUMP) {
                if (touchControls[0] || input.isKeyPressed(android.view.KeyEvent.KEYCODE_DPAD_CENTER)) {
                    bob.velocity.y = Bob.BOB_JUMP_VELOCITY;
                    newState = BobNextState.JUMP;
                    listener.jump();
                }
            }
        }
        else
            bob.velocity.x = 0;
        
        bob.update(deltaTime, newState);
        
        if (bob.state == Bob.BOB_STATE_JUMP)
            heightSoFar = Math.max(bob.position.y, heightSoFar);
        else
            heightSoFar = 0;
    }

    private void updateEnemies(float deltaTime) {
        int len = enemies.size();
        for (int i = 0; i < len; i++) {
            Enemy fly = enemies.get(i);
            fly.update(deltaTime);
        }
    }

    private void updatePlatforms(float deltaTime) {
        int len = platforms.size();
        for (int i = 0; i < len; i++) {
            Platform platform = platforms.get(i);
            platform.update(deltaTime);
            
            /*
            if (platform.state == Platform.PLATFORM_STATE_PULVERIZING && platform.stateTime > Platform.PLATFORM_PULVERIZE_TIME) {
                platforms.remove(platform);
                len = platforms.size();
            }
            */
        }
        
        len = movingPlatforms.size();
        for (int i = 0; i < len; i++) {
            Platform platform = movingPlatforms.get(i);
            platform.update(deltaTime);
        }
    }

    private void updateCoins(float deltaTime) {
        int len = items.size();
        for (int i = 0; i < len; i++) {
            Item coin = items.get(i);
            coin.update(deltaTime);
        }
    }

    private void checkCollisions(float deltaTime) {
        checkSolidBlockCollisions(deltaTime);
        checkPlatformCollisions(deltaTime);
        checkMovingPlatformCollisions(deltaTime);
        checkItemCollisions();
        checkEnemyCollisions();
        checkExitCollisions(deltaTime);
    }

    private void checkPlatformCollisions(float deltaTime) {
        if (onLadder)
            return;
        
        int len = platforms.size();
        for (int i = 0; i < len; i++) {
            Platform platform = platforms.get(i);
            
            if (bob.state == Bob.BOB_STATE_JUMP && heightSoFar < platform.position.y + Platform.PLATFORM_HEIGHT / 2 && platform.type != Platform.PLATFORM_TYPE_SOLID)
                continue;
            
            if (OverlapTester.overlapRectangles(bob.bounds, platform.bounds)) {
                if (platform.type == Platform.PLATFORM_TYPE_SOLID && bob.position.y < platform.position.y && bob.velocity.y > 0) {
                    bob.hitSolidPlatform(platform);
                }
                else if (bob.velocity.y < 0 && bob.bounds.lowerLeft.y > platform.bounds.lowerLeft.y) {
                    onMovingPlatform = bob.hitPlatform(platform);
                    
                    if (onMovingPlatform) {
                        bob.velocity.x += platform.velocity.x;
                    }
                }
                
                break;
            }
            
        }
        
        if (!onLadder && bob.velocity.y != 0 && bob.state != Bob.BOB_STATE_JUMP && bob.state != Bob.BOB_STATE_STOP && !onMovingPlatform)
            bob.setFalling();        
    }
    
    private void checkMovingPlatformCollisions(float deltaTime) {
        int len = movingPlatforms.size();
        for (int i = 0; i < len; i++) {
            Platform platform = movingPlatforms.get(i);
            
            if (bob.state == Bob.BOB_STATE_JUMP && heightSoFar < platform.position.y + Platform.PLATFORM_HEIGHT / 2 && platform.type != Platform.PLATFORM_TYPE_SOLID)
                continue;
            
            if (OverlapTester.overlapRectangles(bob.bounds, platform.bounds)) {
                if (bob.velocity.y <= 0 && bob.bounds.lowerLeft.y > platform.bounds.lowerLeft.y) {
                    onMovingPlatform = bob.hitPlatform(platform);
                    
                    if (onMovingPlatform && ((MovingPlatform)platform).isMoving) {
                        bob.velocity.x += platform.velocity.x;
                    }
                }
                break;
            }            
        }        
    }

    private boolean checkLadderCollsions(float deltaTime, boolean downCheck) {
        boolean onLadder = false;
        if (bob.state == Bob.BOB_STATE_JUMP)
            return false;
        
        int len = ladderBlocks.size();
        for (int i = 0; i < len; i++) {
            LadderBlock ladderBlock = ladderBlocks.get(i);
            
            if (OverlapTester.overlapRectangles(bob.bounds, ladderBlock.bounds)) {
                if (bob.position.x > ladderBlock.bounds.lowerLeft.x && bob.position.x < ladderBlock.bounds.lowerLeft.x + LadderBlock.BLOCK_WIDTH) {
                    if (downCheck && bob.bounds.lowerLeft.y < ladderBlock.bounds.lowerLeft.y) {
                        continue;
                    }
                    else {
                        bob.hitLadderBlock(ladderBlock);
                        onLadder = true;
                        break;
                    }
                }
            }
        }    

        //if (!onLadder) { 
        //    bob.offLadderBlock();
        //    checkLadders = false;
        //}
        
        return onLadder;
    }
    
    private void checkSolidBlockCollisions(float deltaTime) {        
        int len = solidBlocks.size();
        for (int i = 0; i < len; i++) {
            SolidBlock solidBlock = solidBlocks.get(i);
            
            if (OverlapTester.overlapRectangles(bob.bounds, solidBlock.bounds)) {
                bob.hitVerticalSolidBlock(solidBlock);
                break;
            }
        }
        
        len = doors.size();
        for (int i = 0; i < len; i++) {
            Scenery door = doors.get(i);
            
            if (OverlapTester.overlapRectangles(bob.bounds, door.bounds)) {
                if (keys.get(i).state) {
                    doors.remove(i);
                    keys.remove(i);
                }
                else
                    bob.hitDoor(door);
                
                break;
            }
        }
        
        //if (bob.velocity.y != 0 && bob.state != Bob.BOB_STATE_JUMP)
        //    bob.setFalling();        
    }

    /*
    private void checkSquirrelCollisions() {
        int len = squirrels.size();
        for (int i = 0; i < len; i++) {
            Squirrel squirrel = squirrels.get(i);
            if (OverlapTester.overlapRectangles(squirrel.bounds, bob.bounds)) {
                bob.hitSquirrel();
                listener.hit();
            }
        }
    }
    */

    private void checkEnemyCollisions() {
        int len = enemies.size();
        
        for (int i = 0; i < len; i++) {
            Enemy enemy = enemies.get(i);
            
            if (OverlapTester.overlapRectangles(enemy.bounds, bob.bounds)) {
                bob.hitEnemy();
                listener.hit();
            }
        }
    }
    
    private void checkItemCollisions() {
        int len = items.size();
        
        for (int i = 0; i < len; i++) {
            Item coin = items.get(i);
            
            if (OverlapTester.overlapRectangles(bob.bounds, coin.bounds)) {
                items.remove(coin);
                len = items.size();
                listener.coin();
                score += Item.COIN_SCORE;
                itemsCollected ++;
            }

        }

        len = keys.size();
        for (int i = 0; i < len; i++) {
            Scenery myKey = keys.get(i);
            
            if (myKey.state)
                continue;
            
            if (OverlapTester.overlapRectangles(bob.bounds, myKey.bounds)) {
                //keys.remove(myKey);
                //len = keys.size();
                myKey.state = true;
                listener.coin();
                score += Item.COIN_SCORE;
            }
        }
        
        len = switches.size();
        for (int i = 0; i < len; i++) {
            if (switched)
                break;
            
            Scenery mySwitch = switches.get(i);
            
            if (OverlapTester.overlapRectangles(bob.bounds, mySwitch.bounds)) {
                //mySwitch.state = true;
                if (bob.state == Bob.BOB_STATE_JUMP) {
                    mySwitch.state = !mySwitch.state;
                    switched = true;                

                    if (this.level != 8) {
                        Platform platform = movingPlatforms.get(i);
                        //((MovingPlatform)platform).isMoving = true;
                        ((MovingPlatform)platform).isMoving = !((MovingPlatform)platform).isMoving;
                    }
                    else {
                        if (switches.get(0).state && switches.get(1).state && !switches.get(2).state) {
                            ((MovingPlatform)movingPlatforms.get(0)).isMoving = true;
                            ((MovingPlatform)movingPlatforms.get(1)).isMoving = false;
                            ((MovingPlatform)movingPlatforms.get(2)).isMoving = false;
                        }
                        else if (switches.get(1).state && switches.get(2).state) {
                            ((MovingPlatform)movingPlatforms.get(0)).isMoving = false;
                            ((MovingPlatform)movingPlatforms.get(1)).isMoving = false;
                            ((MovingPlatform)movingPlatforms.get(2)).isMoving = true;
                            
                            if (!switches.get(0).state) {
                                ((MovingPlatform)movingPlatforms.get(0)).isMoving = false;
                                ((MovingPlatform)movingPlatforms.get(1)).isMoving = true;
                                ((MovingPlatform)movingPlatforms.get(2)).isMoving = true;
                            }
                        }
                        else {
                            ((MovingPlatform)movingPlatforms.get(0)).isMoving = false;
                            ((MovingPlatform)movingPlatforms.get(1)).isMoving = false;
                            ((MovingPlatform)movingPlatforms.get(2)).isMoving = false;
                        }
                    }
                    
                    listener.highJump();
                }
            }
        }

        if (switched && (bob.state == Bob.BOB_STATE_WALK || bob.state == Bob.BOB_STATE_STOP))
            switched = false;
        
        len = spikes.size();
        for (int i = 0; i < len; i++) {
            Scenery spike = spikes.get(i);
            
            if (OverlapTester.overlapRectangles(bob.bounds, spike.bounds)) {
                bob.hitEnemy();
                listener.hit();
            }
        }
    }

    private void checkExitCollisions(float deltaTime) {        
        if (OverlapTester.overlapRectangles(exit.bounds, bob.bounds)) {
            if (itemsCollected < numItems) {
                if (!itemMessage) {
                    messages.add(new DisplayMessage("Collect all items first!", deltaTime, 4.0f, 40, 160));
                    itemMessage = true;
                }
            }
            else {
                state = WORLD_STATE_NEXT_LEVEL;
                itemsCollected = 0;
            }
        }
        else
            itemMessage = false;
    }
    
    private void checkGameOver() {
        //if (heightSoFar - 7.5f > bob.position.y) {
        //    state = WORLD_STATE_GAME_OVER;
        //}
        if (bob.position.y > 0)
            return;
        
        //lives--;
        
        if (lives == 0)
            state = WORLD_STATE_GAME_OVER;
        else
            state = WORLD_STATE_DIED;
    }
    
    private void createPlatform(float x, float y, int len, TextureRegion start, TextureRegion middle, TextureRegion end, int type, boolean flip) {
        Platform platform = new Platform(type, x++, y, start, false, 0);
        //platform.adjustBoundsX(0.35f);
        platforms.add(platform);
        //x++;
        
        if (len == 1) return;
        
        for (int i = 0; i < (len - 2); i++) {
            platform = new Platform(type, x++, y, middle, false, 0);
            platforms.add(platform);
        }

        platform = new Platform(type, x, y, end, flip, 0);
        //platform.adjustBoundsWidth(0.35f);
        platforms.add(platform);

    }
    
    private void createPlatform(float x, float y, TextureRegion start, int type, boolean flip) {
        Platform platform = new Platform(type, x++, y, start, flip, 0);
        platforms.add(platform);
    }     

    private void createPlatform(float x, float y, int len, TextureRegion texture, int type, boolean flip, float angle) {
        for (int i = 0; i < len; i++) {
            Platform platform = new Platform(type, x++, y, texture, flip, angle);
            platforms.add(platform);
        }
    }     

}